using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Markup;

namespace Common.Core.Configuration
{
	public class GenericExtension : GenericTypeExtension
	{
		public GenericExtension( string typeName ) : base( typeName )
		{}

		public override object ProvideValue(IServiceProvider serviceProvider)
		{
			var type = (Type)base.ProvideValue(serviceProvider);
			var result = Activator.CreateInstance( type );
			return result;
		}
	}

	[ContentProperty( "TypeArguments" ), MarkupExtensionReturnType( typeof(Type) )]
	public class GenericTypeExtension : MarkupExtension
	{
		public GenericTypeExtension( string typeName )
		{
			var items = typeName.Split( new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries );
			TypeName = items.First();
			ProvidedTypes = ( from item in items select item ).Skip( 1 ).ToArray();
		}

		public Collection<Type> TypeArguments
		{
			get { return _typeArguments; }
		}	readonly Collection<Type> _typeArguments = new Collection<Type>();

		public string TypeName { get; set; }

		string[] ProvidedTypes { get; set; }

		// ProvideValue, which returns an object instance of the constructed generic type
		public override object ProvideValue( IServiceProvider serviceProvider )
		{
			var resolver = GetXamlTypeResolver( serviceProvider );
			var result = ResolveType( resolver );
			return result;
		}

		Type ResolveType( IXamlTypeResolver resolver )
		{
			var types = from name in ProvidedTypes 
			            let resolved = resolver.Resolve( name )
			            where resolved != null
			            select resolved;
			var arguments = types.Concat( TypeArguments );
			
			var type = resolver.Resolve( string.Format( "{0}`{1}", TypeName, arguments.Count() ) );

			var result = type != null ? type.MakeGenericType( arguments.ToArray() ) : null;
			return result;
		}

		static IXamlTypeResolver GetXamlTypeResolver( IServiceProvider serviceProvider )
		{
			var xamlTypeResolver = serviceProvider.GetService( typeof(IXamlTypeResolver) ) as IXamlTypeResolver;
			if ( xamlTypeResolver == null )
			{
				throw new Exception( "The Generic markup extension requires an IXamlTypeResolver service provider" );
			}
			return xamlTypeResolver;
		}
	}
}